/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hi.chyl.json;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rtextarea.RTextScrollPane;

/**
 *
 * @author chench
 */
public class JsonPanel extends JPanel
{

    private RSyntaxTextArea editor;
    private JTree nodeTree;
    private JTable valueTable;
    private JSplitPane splitPane;

    public JsonPanel()
    {
        super();
        init();
    }

    public RSyntaxTextArea getEditor()
    {
        return editor;
    }

    public void setEditor(RSyntaxTextArea editor)
    {
        this.editor = editor;
    }

    public JTree getNodeTree()
    {
        return nodeTree;
    }

    public void setNodeTree(JTree nodeTree)
    {
        this.nodeTree = nodeTree;
    }

    public JTable getValueTable()
    {
        return valueTable;
    }

    public void setValueTable(JTable valueTable)
    {
        this.valueTable = valueTable;
    }
    
    public String getNodeValue(String node)
    {
        if (StringUtils.isEmpty(node))
            return null;
        String[] members = node.split("-");
        
        JsonElement jsonEle = MainView.JSON_MAP.get(nodeTree.hashCode());
        if (jsonEle == null || jsonEle.isJsonNull())
            return null;
        JsonObject obj = jsonEle.getAsJsonObject();
        
        for (int i = 0; i < members.length; i++)
        {
            if (i == members.length - 1)
            {
                return obj.get(members[i]).getAsString();
            } else {
                obj = obj.getAsJsonObject(members[i]);
                if (obj == null)
                {
                    return null;
                }
            }
        }
        return null;
    }

    /**
     * 界面初始化
     */
    private void init()
    {
        splitPane = new JSplitPane();
        splitPane.addComponentListener(new ComponentAdapter()
        {

            @Override
            public void componentResized(ComponentEvent e)
            {
                splitPane.setDividerLocation(0.45);
            }
        });

        initJsonEditor();

        RTextScrollPane sp = new RTextScrollPane(editor);
        sp.setFoldIndicatorEnabled(true);    // 显示代码折叠标记
        splitPane.setTopComponent(sp);

        final JSplitPane rightSplitPane = new JSplitPane();
        rightSplitPane.setDividerSize(1);
        rightSplitPane.addComponentListener(new ComponentAdapter()
        {

            @Override
            public void componentResized(ComponentEvent e)
            {
                rightSplitPane.setDividerLocation(0.6);
            }
        });

        initNodeTree();

        rightSplitPane.setLeftComponent(new JScrollPane(nodeTree));
        initValueTable();
        rightSplitPane.setRightComponent(new JScrollPane(valueTable));

        splitPane.setBottomComponent(rightSplitPane);

        setLayout(new BorderLayout());
        add(splitPane);
    }

    private void initJsonEditor()
    {
        editor = new RSyntaxTextArea();
        editor.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);    // JAVASCRIPT 代码样式
        editor.setCodeFoldingEnabled(true);    // 启用代码折叠
        editor.setAntiAliasingEnabled(false);   // 启用字体反锯齿
        editor.setAutoscrolls(true);
        editor.setFont(new Font("宋体", Font.PLAIN, 13));
        //editor.setFont(MainApp.FONT_FIXEDSYS);

        SyntaxScheme scheme = editor.getSyntaxScheme();
//        scheme.getStyle(Token.COMMENT_KEYWORD).foreground = Color.red;
//      scheme.getStyle(Token.DATA_TYPE).foreground = Color.blue;
        scheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).foreground = Color.BLUE;
        scheme.getStyle(Token.LITERAL_NUMBER_DECIMAL_INT).foreground = new Color(164, 0, 0);
        scheme.getStyle(Token.LITERAL_NUMBER_FLOAT).foreground = new Color(164, 0, 0);
        scheme.getStyle(Token.LITERAL_BOOLEAN).foreground = Color.RED;
        scheme.getStyle(Token.OPERATOR).foreground = Color.BLACK;
        editor.revalidate();
//        textArea.addMouseListener(new TextAreaMouseListener());
        editor.setPopupMenu(TextActions.createTextPopMenu());
        
        editor.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e)
            {
                    editor.requestFocus();
            }
            
        });
    }

    private void initNodeTree()
    {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("o-JSON");
        DefaultTreeModel model = new DefaultTreeModel(root);
        nodeTree = new JTree(model);
        nodeTree.addTreeSelectionListener(new TreeSelectionListener()
        {

            public void valueChanged(TreeSelectionEvent evt)
            {
                treeSelection(nodeTree, valueTable);
            }
        });
        setNodeIcon(nodeTree);
        nodeTree.addMouseListener(new TreeMouseListener(nodeTree));
        System.out.println("New HashCode : " + nodeTree.hashCode());
    }

    private void initValueTable()
    {
        String col[] =
        {
            "key", "value"
        };
        DefaultTableModel tm = new DefaultTableModel();
        tm.setColumnCount(2);
        tm.setColumnIdentifiers(col);
        valueTable = new JTable(tm);
        valueTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        valueTable.setAutoscrolls(true);
        valueTable.setMinimumSize(new Dimension(160, 100));
    }

    private void treeSelection(JTree tree, JTable table)
    {
        DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
        if (selNode == null)
        {
            //System.out.println("jTree1ValueChanged:selNode is null");
            return;
        }
        String col[] =
        {
            "key", "value"
        };
        DefaultTableModel tm = (DefaultTableModel) table.getModel();
        tm.setColumnCount(2);
        tm.setColumnIdentifiers(col);
        if (selNode.isLeaf())
        {
            tm.setRowCount(1);
            String arr[] = Kit.pstr(selNode.toString());
            tm.setValueAt(arr[1], 0, 0);
            tm.setValueAt(arr[2], 0, 1);
        } else
        {
            int childCount = selNode.getChildCount();
            tm.setRowCount(childCount);
            for (int i = 0; i < childCount; i++)
            {
                String arr[] = Kit.pstr(selNode.getChildAt(i).toString());
                tm.setValueAt(arr[1], i, 0);
                tm.setValueAt(arr[2], i, 1);
            }
        }
        table.setModel(tm);
        TableColumn column0 = table.getColumnModel().getColumn(0);
        column0.setPreferredWidth(getPreferredWidthForColumn(table, column0));
        TableColumn column1 = table.getColumnModel().getColumn(1);
        column1.setPreferredWidth(getPreferredWidthForColumn(table, column1));
        table.updateUI();
    }

    //[start]自动调列宽
    private int getPreferredWidthForColumn(JTable table, TableColumn col)
    {
        int hw = columnHeaderWidth(table, col);  // hw = header width
        int cw = widestCellInColumn(table, col);  // cw = column width
        return hw > cw ? hw : cw;

    }

    private int columnHeaderWidth(JTable table, TableColumn col)
    {
        TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer();
        Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0);
        return comp.getPreferredSize().width;
    }

    private int widestCellInColumn(JTable table, TableColumn col)
    {
        int c = col.getModelIndex();
        int width = 0, maxw = 0;
        for (int r = 0; r < table.getRowCount(); r++)
        {
            TableCellRenderer renderer = table.getCellRenderer(r, c);
            Component comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, c), false, false, r, c);
            width = comp.getPreferredSize().width;
            maxw = width > maxw ? width : maxw;
        }
        if (maxw < 90)
        {
            maxw = 90;
        }
        return maxw + 10;
    }
    //[end]自动调列宽

    private void setNodeIcon(JTree tree)
    {
        tree.setCellRenderer(new DefaultTreeCellRenderer()
        {
            @Override
            public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
            {
                super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
                String tmp = node.toString();
                if (tmp.startsWith(Kit.sArr))
                {
                    this.setIcon(new ImageIcon(getClass().getResource("resources/a.gif")));
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(Kit.sStr))
                {
                    this.setIcon(new ImageIcon(getClass().getResource("resources/v.gif")));
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(Kit.sObj))
                {
                    this.setIcon(new ImageIcon(getClass().getResource("resources/o.gif")));
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(Kit.sNum))
                {
                    this.setIcon(new ImageIcon(getClass().getResource("resources/n.gif")));
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(Kit.sNull))
                {
                    this.setIcon(new ImageIcon(getClass().getResource("resources/k.gif")));
                    this.setText(tmp.substring(2));
                } else if (tmp.startsWith(Kit.sBool))
                {
                    this.setIcon(new ImageIcon(getClass().getResource("resources/v.gif")));
                    this.setText(tmp.substring(2));
                } else
                {
                    this.setIcon(new ImageIcon(getClass().getResource("resources/v.gif")));
                    this.setText(tmp.substring(2));
                }
                return this;
            }
        });
    }
    
    public boolean parseJson()
    {
        //格式化字符串
        JsonElement jsonEle = null;
        String text = editor.getText();
        try
        {
            JsonParser parser = new JsonParser();
            jsonEle = parser.parse(text);
            if (jsonEle != null && !jsonEle.isJsonNull())
            {
                GsonBuilder gb = new GsonBuilder();
                gb.setPrettyPrinting();
                gb.serializeNulls();
                Gson gson = gb.create();
                String jsonStr = gson.toJson(jsonEle);
                if (jsonStr != null)
                {
                    return true;
                }
                return false;
            } else
            {
                JOptionPane.showMessageDialog(null, "非法JSON字符串！是否缺少开始“{”或结束“}”？", "错误", JOptionPane.ERROR_MESSAGE);
                return false;
            }
        } catch (Exception ex)
        {
            JOptionPane.showMessageDialog(null, "非法JSON字符串！" + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
            return false;
        }
    }

    public boolean formatJson()
    {
        //格式化字符串
        JsonElement jsonEle = null;
        String text = editor.getText();
        try
        {
            JsonParser parser = new JsonParser();
            jsonEle = parser.parse(text);
            if (jsonEle != null && !jsonEle.isJsonNull())
            {
                GsonBuilder gb = new GsonBuilder();
                gb.setPrettyPrinting();
                gb.serializeNulls();
                Gson gson = gb.create();
                String jsonStr = gson.toJson(jsonEle);
                if (jsonStr != null)
                {
                    jsonStr = StringEscapeUtils.unescapeJava(jsonStr);
//                    editor.setText(jsonStr);
                    editor.replaceRange(jsonStr, 0, text.length());
                }
            } else
            {
                JOptionPane.showMessageDialog(null, "非法JSON字符串！是否缺少开始“{”或结束“}”？", "错误", JOptionPane.ERROR_MESSAGE);
                return false;
            }
        } catch (Exception ex)
        {
            JOptionPane.showMessageDialog(null, "非法JSON字符串！" + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
            return false;
        }
        System.gc();

        //创建树节点

//        System.out.println("Put HashCode : " + nodeTree.hashCode() + " . TabTitle : " + getTabTitle() +  " !");
        MainView.JSON_MAP.put(nodeTree.hashCode(), jsonEle);
        DefaultMutableTreeNode root = Kit.objNode("JSON");
        DefaultTreeModel model = (DefaultTreeModel) nodeTree.getModel();
        try
        {
            createJsonTree(jsonEle, root);
            model.setRoot(root);
            setNodeIcon(nodeTree);
        } catch (Exception ex)
        {
            root.removeAllChildren();
            model.setRoot(root);
            JOptionPane.showMessageDialog(null, "创建json树失败！" + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
        }
        System.gc();
        return true;
    }

    /**
     * 构造json树结构.
     * @param obj JsonElement
     * @param pNode DefaultMutableTreeNode
     */
    private void createJsonTree(JsonElement obj, DefaultMutableTreeNode pNode)
    {
        if (obj.isJsonNull())
        {
            pNode.add(Kit.nullNode("NULL"));
        } else if (obj.isJsonArray())
        {
            createJsonArray(obj.getAsJsonArray(), pNode, "[0]");
        } else if (obj.isJsonObject())
        {
            JsonObject child = obj.getAsJsonObject();
            // DefaultMutableTreeNode node = Kit.objNode(key);
            createJsonObject(child, pNode);
            // pNode.add(node);
        } else if (obj.isJsonPrimitive())
        {
            JsonPrimitive pri = obj.getAsJsonPrimitive();
            formatJsonPrimitive("PRI", pri, pNode);
        }
    }

    /**
     * 处理json数组.
     * @param arr
     * @param pNode
     * @param key
     */
    private void createJsonArray(JsonArray arr, DefaultMutableTreeNode pNode, String key)
    {
        int index = 0;
        DefaultMutableTreeNode child = Kit.arrNode(key);
        for (Iterator it = arr.iterator(); it.hasNext();)
        {
            JsonElement el = (JsonElement) it.next();
            if (el.isJsonObject())
            {
                JsonObject obj = el.getAsJsonObject();
                DefaultMutableTreeNode node = Kit.objNode(index);
                createJsonObject(obj, node);
                child.add(node);
            } else if (el.isJsonArray())
            {
                JsonArray lst = el.getAsJsonArray();
                createJsonArray(lst, child, Kit.fkey(index));
            } else if (el.isJsonNull())
            {
                child.add(Kit.nullNode(index));
            } else if (el.isJsonPrimitive())
            {
                formatJsonPrimitive(Kit.fkey(index), el.getAsJsonPrimitive(), child);
            }
            ++index;
        }
        pNode.add(child);
    }

    /**
     * 处理jsoon对象.
     * @param obj
     * @param pNode
     */
    private void createJsonObject(JsonObject obj, DefaultMutableTreeNode pNode)
    {
        for (Map.Entry<String, JsonElement> el : obj.entrySet())
        {
            String key = el.getKey();
            JsonElement val = el.getValue();
            if (val.isJsonNull())
            {
                pNode.add(Kit.nullNode(key));
            } else if (val.isJsonArray())
            {
                createJsonArray(val.getAsJsonArray(), pNode, key);
            } else if (val.isJsonObject())
            {
                JsonObject child = val.getAsJsonObject();
                DefaultMutableTreeNode node = Kit.objNode(key);
                createJsonObject(child, node);
                pNode.add(node);
            } else if (val.isJsonPrimitive())
            {
                JsonPrimitive pri = val.getAsJsonPrimitive();
                formatJsonPrimitive(key, pri, pNode);
            }
        }

    }

    private void formatJsonPrimitive(String key, JsonPrimitive pri, DefaultMutableTreeNode pNode)
    {
        if (pri.isJsonNull())
        {
            pNode.add(Kit.nullNode(key));
        } else if (pri.isNumber())
        {
            pNode.add(Kit.numNode(key, pri.getAsString()));
        } else if (pri.isBoolean())
        {
            pNode.add(Kit.boolNode(key, pri.getAsBoolean()));
        } else if (pri.isString())
        {
            pNode.add(Kit.strNode(key, pri.getAsString()));
        } else if (pri.isJsonArray())
        {
            createJsonArray(pri.getAsJsonArray(), pNode, key);
        } else if (pri.isJsonObject())
        {
            JsonObject child = pri.getAsJsonObject();
            DefaultMutableTreeNode node = Kit.objNode(key);
            createJsonObject(child, node);
            pNode.add(node);
        } else if (pri.isJsonPrimitive())
        {
            formatJsonPrimitive(key, pri.getAsJsonPrimitive(), pNode);
        }
    }
    
    
    public void changeLayout() {
        if (splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT) {
            splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
            splitPane.setDividerLocation(0.45);
        } else {
            splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
            splitPane.setDividerLocation(0.45);
        }
    }
}
